﻿using Abp;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.BackgroundJobs;
using Abp.Collections.Extensions;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.Runtime.Caching;
using Abp.Runtime.Session;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.ContentManagement.Dto;
using Mt.Site.Application.ContentManagement.FrontEnd.Dto;
using Mt.Site.Application.Dto;
using Mt.Site.Application.Dto.Add;
using Mt.Site.Core.BackGroundJobs;
using Mt.Site.Core.BackGroundJobs.ContentManagement;
using Mt.Site.Core.BackGroundJobs.Storage;
using Mt.Site.Core.Base.Authorization;
using Mt.Site.Core.Base.Reflection;
using Mt.Site.Core.ContentManagement;
using Mt.Site.Core.ContentManagement.Entities;
using Mt.Site.Core.ContentManagement.Permissions;
using Mt.Site.Core.ContentManagement.Preview;
using Mt.Site.Core.ContentManagement.Values;
using Mt.Site.Core.DiyContent.Form;
using Mt.Site.Core.Storage;
using Mt.Site.Core.Synchronization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace Mt.Site.Application.ContentManagement
{
    public class ContentAppService : MtSiteAppServiceBase, IContentAppService
    {
        private readonly IContentManager _contentManager;

        private readonly IFileObjectManager _fileObjectManager;

        private readonly IBackgroundJobManager _backgroundJobManager;

        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IRepository<Content, Guid> _contentRepository;
        private readonly IPermissionManager _permissionManager;
        private readonly IPreviewDataManager _previewDataManager;

        private readonly ICacheManager _cacheManager;

        public ContentAppService(
            IContentManager contentManager,
            IFileObjectManager fileObjectManager,
            IBackgroundJobManager backgroundJobManager,
            IUnitOfWorkManager unitOfWorkManager,
            IRepository<Content, Guid> contentRepository,
            IPermissionManager permissionManager,
            IPreviewDataManager previewDataManager,
            ICacheManager cacheManager)
        {
            _contentManager = contentManager;
            _fileObjectManager = fileObjectManager;
            _backgroundJobManager = backgroundJobManager;
            _unitOfWorkManager = unitOfWorkManager;
            _contentRepository = contentRepository;
            _permissionManager = permissionManager;
            _previewDataManager = previewDataManager;

            _cacheManager = cacheManager;
        }

        [AbpAuthorize]
        public async Task ResetListApiCacheAsync()
        {
            await _cacheManager.GetCache<string, PagedResultDto<_ContentListDto>>(ApiCacheNames.ContentPagedList).ClearAsync();
        }

        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Create, ContentPermissions.Pages_Manage_Content_Update, RequireAllPermissions = false)]
        public async Task<Guid> CreateOrUpdateAsync(CreateOrUpdateInput input)
        {
            Guid id;
            if (input.Id.HasValue)
            {
                id = await _contentManager.UpdateAsync(input.Values, input.Id.Value, input.MenuId, input.CustomKey, input.RelateId);


                if (MtSiteConsts2.BackgroundJobs)
                {
                    _backgroundJobManager.Enqueue<SynchorizationContentJob, BaseAndMenuIdArgs>(
                        new BaseAndMenuIdArgs
                        {
                            FromLanguage = AbpSession.CultureName(),
                            FromTenantId = AbpSession.GetTenantId(),
                            Id = id,
                            MenuId = input.MenuId,
                            OperateType = OptionType.Update
                        });
                }

                await ResetListApiCacheAsync();

                return id;
            }
            else
            {
                id = await _contentManager.CreateAsync(input.Values, input.MenuId, input.CustomKey, input.RelateId);

                IList<Guid> fileIds = null;
                if (input.RandomId.HasValue)
                {
                    fileIds = await _fileObjectManager.BuildRelationships(id, input.RandomId.Value);
                }


                if (MtSiteConsts2.BackgroundJobs)
                {
                    _backgroundJobManager.Enqueue<SynchorizationContentJob, BaseAndMenuIdArgs>(
                    new BaseAndMenuIdArgs
                    {
                        FromLanguage = AbpSession.CultureName(),
                        FromTenantId = AbpSession.GetTenantId(),
                        Id = id,
                        MenuId = input.MenuId,
                        OperateType = OptionType.Create
                    });

                    if (fileIds != null)
                    {
                        foreach (var item in fileIds)
                        {
                            _backgroundJobManager.Enqueue<SynchorizationFileObjectJob, BaseAndMenuIdArgs>(
                                        new BaseAndMenuIdArgs
                                        {
                                            FromLanguage = AbpSession.CultureName(),
                                            FromTenantId = AbpSession.GetTenantId(),
                                            Id = item,
                                            OperateType = OptionType.Create
                                        }, BackgroundJobPriority.BelowNormal);
                        }
                    }
                }


                await ResetListApiCacheAsync();

                return id;
            }
        }

        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Delete)]
        public async Task DeleteAysnc(MenuAndIdInput<Guid> input)
        {
            var item = await _contentManager.DeleteAysnc(input.Id);

            await ResetListApiCacheAsync();

            if (item != null && MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationContentJob, BaseAndMenuIdArgs>(
                    new BaseAndMenuIdArgs
                    {
                        FromLanguage = AbpSession.CultureName(),
                        FromTenantId = AbpSession.GetTenantId(),
                        Id = item.Id,
                        MenuId = item.MenuId,
                        OperateType = OptionType.Delete
                    });
            }
        }

        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Delete)]
        public async Task DeleteListAysnc(EntityIdListWithMenuIdDto input)
        {
            var itemList = await _contentManager.DeleteListAysnc(input.List);

            await ResetListApiCacheAsync();

            if (MtSiteConsts2.BackgroundJobs)
            {
                foreach (var item in itemList)
                {
                    _backgroundJobManager.Enqueue<SynchorizationContentJob, BaseAndMenuIdArgs>(
                        new BaseAndMenuIdArgs
                        {
                            FromLanguage = AbpSession.CultureName(),
                            FromTenantId = AbpSession.GetTenantId(),
                            Id = item.Id,
                            MenuId = item.MenuId,
                            OperateType = OptionType.Delete
                        });
                }
            }
        }

        [AbpAuthorize(ContentPermissions.Pages_Manage_Content)]
        public async Task<ContentConfig> GetConfigAsync(GetConfigInput input)
        {
            return await _contentManager.GetConfig(input.CustomKey, input.MenuId);
        }

        [AbpAuthorize(ContentPermissions.Pages_Manage_Content)]
        public async Task<ConfigAndPermisions> GetConfigAndPermisionsAsync(GetConfigInput input)
        {
            var config = await _contentManager.GetConfig(input.CustomKey, input.MenuId);

            var permissions = _permissionManager.GetCustomMenuPermissions(ContentPermissionProvider.ModuleName);
            var list = new Dictionary<string, bool>();
            var userId = AbpSession.GetUserId();
            foreach (var p in permissions)
            {
                list.Add(p.Name, PermissionChecker.IsGranted(userId, p, input.MenuId));
            }
            return new ConfigAndPermisions
            {
                Config = config,
                Permissions = list
            };
        }

        [HttpPost]
        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Create, ContentPermissions.Pages_Manage_Content_Update, RequireAllPermissions = false)]
        public async Task<Dictionary<string, object>> GetContentForEditAsync(GetContentForEditInput input)
        {
            return await _contentManager.GetContentForEdit(input.Id, input.MenuId, input.CustomKey, input.RelateId);
        }

        [HttpPost]
        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Query)]
        public async Task<PagedContent<Dictionary<string, object>>> GetContentListByPagedAsync(PagedContentInput input)
        {
            input.CustomKey = await _contentManager.GetCustomKey(input.CustomKey, input.MenuId);

            Func<IQueryable<Content>, IQueryable<Content>> func = (q) =>
            {
                q = q.Where(t => t.MenuId == input.MenuId && t.RelateId == input.RelateId && t.Key == input.CustomKey)
                     .WhereIf(!input.Filter.IsNullOrEmpty(), t => t.Title.Contains(input.Filter))
                     .WhereIf(input.InfoState == MtSiteConsts2.Values.ActiveState, t => t.IsActive == true)
                     .WhereIf(input.InfoState == MtSiteConsts2.Values.UnActiveState, t => t.IsActive == false);

                q = !input.Sorting.IsNullOrEmpty()
                    ? q.OrderBy(input.Sorting)
                    : q.OrderByDescending(t => t.Order).ThenByDescending(t => t.PublishTime).ThenByDescending(t => t.CreationTime);
                return q;
            };

            return await _contentManager.GetContentListByPagedAsync(func, input.SkipCount, input.MaxResultCount, input.CustomKey, input.MenuId, input.RelateId);
        }

        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Update)]
        public async Task SetNewOrderNumberAsync(NewSortNumberWithMenuIdInput input)
        {
            var item = await _contentManager.SetNewOrderNumberAsync(input.Id, input.NewNumber);

            await ResetListApiCacheAsync();

            if (item != null && MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationContentJob, BaseAndMenuIdArgs>(
                    new BaseAndMenuIdArgs
                    {
                        FromLanguage = AbpSession.CultureName(),
                        FromTenantId = AbpSession.GetTenantId(),
                        Id = item.Id,
                        MenuId = item.MenuId,
                        OperateType = OptionType.Update
                    });
            }
        }

        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Publish)]
        public async Task SetStatusAsync(SetStatusWithMenuIdInput input)
        {
            var itemList = await _contentManager.SetStatus(input.IdList, input.IsActive);

            await ResetListApiCacheAsync();

            if (MtSiteConsts2.BackgroundJobs)
            {
                foreach (var item in itemList)
                {
                    _backgroundJobManager.Enqueue<SynchorizationContentJob, BaseAndMenuIdArgs>(
                        new BaseAndMenuIdArgs
                        {
                            FromLanguage = AbpSession.CultureName(),
                            FromTenantId = AbpSession.GetTenantId(),
                            Id = item.Id,
                            MenuId = item.MenuId,
                            OperateType = OptionType.Update
                        });
                }
            }
        }

        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Update)]
        public async Task SwapObjectSortAsync(SwapSortNumberWithMenuIdInput input)
        {
            var idList = input.IdOrderList.Select(c => c.Id).ToList();
            var orderList = input.IdOrderList.Select(c => c.Order).ToList();

            if (idList.Count == 0)
            {
                return;
            }

            var itemList = await _contentManager.SwapObjectSortAsync(idList, orderList);

            await ResetListApiCacheAsync();

            if (MtSiteConsts2.BackgroundJobs)
            {
                foreach (var item in itemList)
                {
                    _backgroundJobManager.Enqueue<SynchorizationContentJob, BaseAndMenuIdArgs>(
                        new BaseAndMenuIdArgs
                        {
                            FromLanguage = AbpSession.CultureName(),
                            FromTenantId = AbpSession.GetTenantId(),
                            Id = item.Id,
                            MenuId = item.MenuId,
                            OperateType = OptionType.Update
                        });
                }
            }
        }

        public ContentSelectionDto GetContentSelectionDto()
        {
            ContentSelectionDto dto = new ContentSelectionDto();

            var list = new List<string>();
            foreach (FieldType item in Enum.GetValues(typeof(FieldType)))
            {
                list.Add(item.ToString("G"));
            }
            dto.CtrlTypes = list;

            var properties = TypeHelper.GetPrimitiveProperties<Content>();
            dto.Fields = properties.Select(c =>
            {
                var item = new NameValue();
                item.Name = c.Key.ToCamelCase();

                string type = c.Value.IsNullableType() ? c.Value.GetUnderlyingType().FullName : c.Value.FullName;
                item.Value = type.Substring(type.LastIndexOf('.') + 1);
                if (item.Value == "Int64" && item.Name != "menuId" && item.Name != "auxiliaryId")
                {
                    item.Value = "DateTime";
                }

                return item;
            }).ToList();

            dto.SpecilField = _contentManager.GetSpecialFields();

            return dto;
        }


        #region 回收站
        /// <summary>
        /// 获取软删除的数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Query)]
        public async Task<PagedResultDto<PagedContentSoftDelInput>> GetContentListSoftDelAsync(PagedContentInput input)
        {
            using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
            {
                var query = _contentRepository.GetAll()
                            .Where(t => t.IsDeleted)
                            .WhereIf(!input.Filter.IsNullOrEmpty(), t => t.Title.Contains(input.Filter) || t.Body.Contains(input.Filter))
                            .OrderByDescending(t => t.DeletionTime);
                var pageCount = query.Count();
                var List = await query.PageBy(input).ToListAsync();
                return new PagedResultDto<PagedContentSoftDelInput>(pageCount, ObjectMapper.Map<List<PagedContentSoftDelInput>>(List));
            }
        }

        /// <summary>
        /// 撤销删除数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AbpAuthorize(ContentPermissions.Pages_Manage_Content_Update)]
        public async Task RecoverContentAsync(EntityIdListDto input)
        {
            await _contentManager.RecoverContentAsync(input.List);
        }
        #endregion
    }
}
